'use strict';

var config = require('./build/build.config.js');
var karmaConfig = require('./build/karma.config.js');
var protractorConfig = require('./build/protractor.config.js');
var gulp = require('gulp');
var gulpUtil = require('gulp-util');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var $ = require('gulp-load-plugins')();
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var pkg = require('./package');
var KarmaServer = require('karma').Server;
var del = require('del');
var _ = require('lodash');
var watchify = require('watchify');
var browserify = require('browserify');
var browserifyShim = require('browserify-shim');
var browserifyNgAnnotate = require('browserify-ngannotate');
var ngHtml2Js = require('browserify-ng-html2js');
/* jshint camelcase:false*/
var webdriverStandalone = require('gulp-protractor').webdriver_standalone;
var webdriverUpdate = require('gulp-protractor').webdriver_update;
/*var modRewrite = require('connect-modrewrite');*/

// Additional gulp argumets
var isProd = !!gulpUtil.env.production; // ex. gulp watch --production
var watchifyEnabled = !!gulpUtil.env.watchifyEnabled; // ex. gulp watch --watchifyEnabled

var browserKeys = Object.keys(pkg.browser) || [];

//update webdriver if necessary, this task will be used by e2e task
gulp.task('webdriver:update', webdriverUpdate);

// run unit tests and watch files
gulp.task('tdd', function(cb) {
    new KarmaServer(_.assign({}, karmaConfig, {
        singleRun: false,
        action: 'watch',
        browsers: ['PhantomJS']
    }), cb).start();
});

// run unit tests with travis CI
gulp.task('travis', ['build'], function(cb) {
    new KarmaServer(_.assign({}, karmaConfig, {
        singleRun: true,
        browsers: ['PhantomJS']
    }), cb).start();
});

// optimize images and put them in the dist folder
gulp.task('images', function() {
    console.log(config.images);
    return gulp.src(config.images)
        .pipe($.imagemin({
            progressive: true,
            interlaced: true
        }))
        .pipe(gulp.dest(config.dist + '/assets/images'))
        .pipe($.size({
            title: 'images'
        }));
});

//generate css files from scss sources
gulp.task('sass', function() {
    return $.rubySass(config.mainScss, { sourcemap: true })
        .on('error', function(err) {
            console.log(err.message);
        })
        .pipe(gulp.dest(config.distStyles))
        .pipe($.size({
            title: 'sass'
        }));
});

//build files for creating a dist release
gulp.task('build:dist', ['clean'], function(cb) {
    runSequence(['build', 'copy', 'copy:assets', 'fonts', 'images'], 'test:unit', cb);
});

gulp.task('build-Release', function(cb) {
    runSequence('build:dist', cb);
});

//build files for development
gulp.task('build', function(cb) {
    runSequence(['jshint', 'sass', 'css', 'browserify-vendor', 'browserify-app'], cb);
});

gulp.task('build-Debug', function(cb) {
    runSequence('build', cb);
});

//generate a minified css files and sourcemaps
gulp.task('css', function() {
    return gulp.src(pkg.cssFiles)
        .pipe($.concat('vendor.css'))
        .pipe($.if(!isProd, $.sourcemaps.init()))
        .pipe($.csso())
        .pipe($.if(!isProd, $.sourcemaps.write()))
        .pipe(gulp.dest(config.distStyles))
        .pipe($.size({
            title: 'css'
        }));
});

//copy assets in dist folder
gulp.task('copy:assets', function() {
    return gulp.src(config.assets, {
        dot: true
    }).pipe(gulp.dest(config.dist + '/assets'))
        .pipe($.size({
            title: 'copy:assets'
        }));
});

//copy assets in dist folder
gulp.task('copy', function() {
    return gulp.src([
        config.base + '/*',
        '!' + config.base + '/*.html',
        '!' + config.base + '/*.js',
        '!' + config.base + '/*.scss',
        '!' + config.base + '/components',
        '!' + config.base + '/vendor',
        '!' + config.base + '/test'
    ]).pipe(gulp.dest(config.dist))
        .pipe($.size({
            title: 'copy'
        }));
});

//copy html templates in dist folder
gulp.task('copy:templates', function() {
    return gulp.src([
        config.base + '/components/**/*.html'
    ]).pipe(gulp.dest(config.dist+'/components'))
        .pipe($.size({
            title: 'copy components'
        }));
});

// Copy fontawesome fonts
gulp.task('fonts', function() {
    return gulp.src([
        config.base + '/vendor/font-awesome/fonts/fontawesome-webfont.*'
    ]).pipe(gulp.dest(config.dist+'/fonts'));
});

//clean temporary directories
gulp.task('clean', del.bind(null, [config.dist]));

//lint files
gulp.task('jshint', function() {
    return gulp.src(config.js)
        .pipe(reload({
            stream: true,
            once: true
        }))
        .pipe($.jshint())
        .pipe($.jshint.reporter('jshint-stylish'))
        .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

/* tasks supposed to be public */


//default task
gulp.task('default', ['watch']); //

//run unit tests and exit
gulp.task('test:unit', ['build'], function(cb) {
    new KarmaServer(_.assign({}, karmaConfig, {
        singleRun: true
    }), cb).start();
});

// Run e2e tests using protractor, make sure serve task is running.
gulp.task('test:e2e', ['webdriver:update'], function() {
    return gulp.src(protractorConfig.config.specs)
        .pipe($.protractor.protractor({
            configFile: 'build/protractor.config.js'
        }))
        .on('error', function(e) {
            throw e;
        });
});

//run the server, watch for file changes and redo tests.
gulp.task('serve:tdd', function(cb) {
    runSequence(['watch', 'tdd']);
});

//run the server after having built generated files, and watch for changes
gulp.task('serve', ['build'], function() {
    browserSync({
        notify: false,
        proxy: config.serverUrl,
        logPrefix: pkg.name
        /*server: {
         baseDir:  ['build', 'app'],
         middleware: [
         modRewrite([
         '!\\.\\w+$ /index.html [L]'
         ])
         ]
         }*/
    });
});

//run the app packed in the dist folder
gulp.task('serve:dist', ['build:dist'], function() {
    browserSync({
        notify: false,
        proxy: config.serverUrl
        /*server: {
         baseDir:  [config.dist],
         middleware: [
         modRewrite([
         '!\\.\\w+$ /index.html [L]'
         ])
         ]
         }*/
    });
});

gulp.task('browserify-app', function () {

    var bundler = browserify({
        entries: config.baseModulePath,
        debug: !isProd,
        cache: {},
        packageCache: {}
    });

    if (!isProd && watchifyEnabled) {
        bundler = watchify(bundler);
        bundler.on('update', rebundle);
    }

    bundler.transform(ngHtml2Js({module: 'templates', requireAngular: true}));

    function rebundle() {
        browserKeys.forEach(function (key) {
            bundler.external(key);
        });

        return bundler.bundle()
            .on('error', function (err) {
                console.log(err.message);
                this.emit('end');
            })
            .pipe(source(config.base + '.js'))
            .pipe(buffer())
            .pipe($.if(isProd, $.uglify({
                mangle: false
            })))
            .pipe(gulp.dest(config.distScripts))
            .pipe(reload({
                stream: true,
                once: true
            }))
            .on('error', function(e){
                console.log(e);
            });
    }

    return rebundle();
});

gulp.task('browserify-vendor', function () {

    var bundler = browserify({
        debug: false,
        cache: {},
        packageCache: {}
    });

    bundler.transform(browserifyShim);

    if (!isProd && watchifyEnabled) {
        bundler = watchify(bundler);
        bundler.on('update', rebundle);
    }

    function rebundle() {
        browserKeys.forEach(function (key) {
            var path = pkg.browser[key];
            bundler.require(path, {
                expose: key
            });
        });

        return bundler.bundle()
            .on('error', function (err) {
                console.log(err.message);
                this.emit('end');
            })
            .pipe(source('vendor.js'))
            .pipe(gulp.dest(config.distScripts))
            .pipe(reload({
                stream: true,
                once: true
            }))
            .on('error', function(e){
                console.log(e);
            });
    }

    return rebundle();
});

gulp.task('watch', ['serve'], function() {
    gulp.watch(config.scss, ['sass', reload]);
    gulp.watch(config.js, ['jshint']);
    gulp.watch(config.assets, reload);
});
